#include "../Headers/types.h"
#include "../Headers/config.h"
#include "../Headers/log.h"
#include "../Headers/authr.h"
#include "fs.client.h"
#include "fs.rpc.h"
#include "fs.file.h"
#include <string>
#include <memory.h>
#include <iostream>
#include <sstream>
#define	PACKAGE_SIZE 255

using namespace TOB;

std::string toString(uint32 i);
std::string stem(std::string path);

FS_Client::FS_Client(Nameserver::IP nip, std::string username, std::string password) : 
				 AuthRequired(nip, username, password) { 
	std::string 		addr;
	Nameserver::IP 	ip;
	Nameserver::convertIP(nip, addr);
  Nameserver ns = Nameserver(addr);
	if (ns.requestAddress(Server::FileSharing, ip) != Code::Okay) { Log::error("Couldn't connect to Nameserver"); return; }
	Nameserver::convertIP(ip, addr);

	clnt = clnt_create(addr.c_str(), FS_PROG, FS_VERSION, "tcp");
	if (clnt == nullptr) { return; }
}
FS_Client::~FS_Client() {
	if (clnt != NULL) { clnt_destroy(clnt); }	
}

Code::Type FS_Client::upload(std::string fileName, uint32 * idStorage) {	
	request r;
	package_fs p;
	uint16 rbytes;
	uint32 fileSize = f.get_file_size(fileName);
	char buf[PACKAGE_SIZE];
	Log::event("Try to upload file " + fileName);
	if (f.exists(fileName) != Code::Okay) { return Code::FS_FunctionCallFailure; }
	if (clnt == NULL) { Log::error("Clnt create failure"); return Code::FS_FunctionCallFailure; }
	r = upload_request(fileName);
	if (r.count == 0) { return Code::FS_FunctionCallFailure; }
	p.fileId  = r.fileId;
	*idStorage = r.fileId;
	for (uint32 i = 0; i < r.count; i++) {
		if (f.read(fileName, buf, PACKAGE_SIZE, i * PACKAGE_SIZE, &rbytes) != Code::Okay) { 
			return Code::FS_FunctionCallFailure;   
		}
		memset(p.data, 0, PACKAGE_SIZE);
		memcpy(p.data, buf, PACKAGE_SIZE);
		p.count = rbytes;
		onProgress( (float) i * 100 / r.count, fileSize);
		upload_file_1(&p, clnt);
	}
	onProgress( 1, fileSize);
	Log::event("File uploaded succesfully " + fileName);
	return Code::Okay;
}

request FS_Client::upload_request(std::string fileName) {
	request * r;
	auth a;
	Log::event("Try to send upload_request for " + fileName);
	a.nsip = nsip;
	memset(a.data, 0, PACKAGE_SIZE);
	memcpy(a.data, stem(fileName).c_str(), stem(fileName).length());
	memcpy(a.username, username.c_str(), username.length());
	memcpy(a.password, password.c_str(), password.length());
	a.count = f.get_file_size(fileName);	
	r 		= upload_request_1(&a, clnt);
	Log::event("Upload_request sended succefully ( " + fileName + " )");
	return *(r);
}

Code::Type FS_Client::download(uint32 fileId, std::string& src, std::string path) {
	package_fs * p;
	auth    	a;
	request   r;
	package_fs * sp;
	std::string fileName;	
	Log::event("Try to download file");
	memcpy(a.username, username.c_str(), username.length());
	memcpy(a.password, password.c_str(), password.length());
	a.nsip   = nsip;
	a.fileId = fileId;
	p 		   = download_request_1(&a, clnt);
	if (p->count == 0) { Log::error("Download failure"); return Code::FS_FunctionCallFailure; }
	fileName = std::string(p->data);
	src = std::string(p->data);
	r.fileId = fileId;	
	for (uint32 i = 0; i < p->count; i++) {
		r.count = i * PACKAGE_SIZE;
		sp		  = download_1(&r, clnt);	
		onProgress( (float) i * 100 / r.count, p->count * PACKAGE_SIZE);		
		f.write(path + fileName, sp->data, sp->count);		
	}		
	onProgress( 1, p->count * PACKAGE_SIZE);		
	Log::event("File downloaded succefully");
	return Code::Okay;
}

std::string toString(uint32 i) {
	std::stringstream ss;
	std::string result;
	ss << i;
	ss >> result;
	return result;
}

std::string stem(std::string path) {
	std::string fileName;
#if defined __linux__
	char d = '/';
#elif defined __WIN32 || defined _WIN32 || defined _WIN64
	char d = '\\';
#endif
	size_t pos = path.find_last_of(d);
	if (pos != std::string::npos)
		fileName.assign(path.begin() + pos + 1, path.end());
	else 
		fileName = path;
	return fileName;
}
